#include "CocosPCH.h"
#include "CoCSWF.h"
#include "Utility.h"
#include "CoCShapeNode.h"
#include "CoCMovieClipNode.h"
#include "CoCImageNode.h"
#include <io.h>
#include <direct.h>

#include "ByteBuffer/ByteBuffer.h"

#include "lzma/Precomp.h"

#include "lzma/Alloc.h"
#include "lzma/7zFile.h"
#include "lzma/7zVersion.h"
#include "lzma/LzmaDec.h"
#include "lzma/LzmaEnc.h"

static void *SzAlloc(void *p, size_t size) { p = p; return MyAlloc(size); }
static void SzFree(void *p, void *address) { p = p; MyFree(address); }
static ISzAlloc g_Alloc = { SzAlloc, SzFree };

#define IN_BUF_SIZE (1 << 16)
#define OUT_BUF_SIZE (1 << 16)

static SRes Decode2(CLzmaDec *state, ByteBuffer *outStream, ByteBuffer *inStream,
    UInt64 unpackSize)
{
    int thereIsSize = (unpackSize != (UInt64)(Int64)-1);
    Byte inBuf[IN_BUF_SIZE];
    Byte outBuf[OUT_BUF_SIZE];
    size_t inPos = 0, inSize = 0, outPos = 0;
    LzmaDec_Init(state);
    for (;;)
    {
        if (inPos == inSize)
        {
            inSize = IN_BUF_SIZE;
            inSize = inStream->read(inBuf, inSize);
            if (inSize == 0)
                return SZ_OK;

            inPos = 0;
        }
        {
            SRes res;
            SizeT inProcessed = inSize - inPos;
            SizeT outProcessed = OUT_BUF_SIZE - outPos;
            ELzmaFinishMode finishMode = LZMA_FINISH_ANY;
            ELzmaStatus status;
            if (thereIsSize && outProcessed > unpackSize)
            {
                outProcessed = (SizeT)unpackSize;
                finishMode = LZMA_FINISH_END;
            }

            res = LzmaDec_DecodeToBuf(state, outBuf + outPos, &outProcessed,
                inBuf + inPos, &inProcessed, finishMode, &status);
            inPos += inProcessed;
            outPos += outProcessed;
            unpackSize -= outProcessed;

            if (outStream)
                if (outStream->write(outBuf, outPos) != outPos)
                    return SZ_ERROR_WRITE;

            outPos = 0;

            if (res != SZ_OK || (thereIsSize && unpackSize == 0))
                return res;

            if (inProcessed == 0 && outProcessed == 0)
            {
                if (thereIsSize || status != LZMA_STATUS_FINISHED_WITH_MARK)
                    return SZ_ERROR_DATA;
                return res;
            }
        }
    }
}

static SRes Decode(ByteBuffer *outStream, ByteBuffer *inStream)
{
    UInt64 unpackSize;
    int i;
    SRes res = 0;

    CLzmaDec state;

    /* header: 5 bytes of LZMA properties and 8 bytes of uncompressed size */
    unsigned char header[LZMA_PROPS_SIZE + 4];

    /* Read and parse header */

    inStream->read(header, sizeof(header));

    unpackSize = 0;
    for (i = 0; i < 8; i++)
        unpackSize += (UInt64)header[LZMA_PROPS_SIZE + i] << (i * 8);

    LzmaDec_Construct(&state);
    RINOK(LzmaDec_Allocate(&state, header, LZMA_PROPS_SIZE, &g_Alloc));
    res = Decode2(&state, outStream, inStream, unpackSize);
    LzmaDec_Free(&state, &g_Alloc);
    return res;
}



CoCSWF::CoCSWF(void)
{
}


CoCSWF::~CoCSWF(void)
{
	Reset();
}

void CoCSWF::Reset()
{
	for(size_t i = 0; i < m_vecTextures.size(); i++)
	{
		delete m_vecTextures[i];
	}
	m_vecTextures.clear();
	
	for(size_t i = 0; i < m_vecMatrix.size(); i++)
	{
		delete m_vecMatrix[i];
	}
	m_vecMatrix.clear();
	
	for(size_t i = 0; i < m_vecColorTransform.size(); i++)
	{
		delete m_vecColorTransform[i];
	}
	m_vecColorTransform.clear();
		
	for(size_t i = 0; i < m_vecShap.size(); i++)
	{
		delete m_vecShap[i];
	}
	m_vecShap.clear();
	for(size_t i = 0; i < m_vecMovieClip.size(); i++)
	{
		delete m_vecMovieClip[i];
	}
	m_vecMovieClip.clear();
	
	m_ExportIDs.clear();
	m_ExportNames.clear();
	m_ExportMovieClip.clear();
	m_mapExportIndex.clear();

	m_mapShapIndex.clear();
}

bool CoCSWF::LoadFromFile(const char *pFileName)
{
	std::string strName = pFileName;
	int nPos = strName.find_last_of("/");
	m_strFileName = strName.substr(nPos+1);

    Data data = FileUtils::getInstance()->getDataFromFile(pFileName);

    ByteBuffer inStream(data.getSize());
    ByteBuffer outStream(data.getSize(), OUT_BUF_SIZE);

    inStream.write(data.getBytes(), data.getSize());

    bool bRet;
    if (SZ_OK == Decode(&outStream, &inStream))
    {
        bRet = LoadFromMemory(outStream.data());
    }
    else
    {
        bRet = LoadFromMemory(data.getBytes());
    }

    //bool bRet = LoadFromMemory(data.getBytes());

	return bRet;
}

bool CoCSWF::LoadFromMemory(void *data)
{	
	Reset();

	int nReadPos = 0;
	nReadPos += 12;
	nReadPos += 5;
	
	unsigned short nExportCount;
	Utility_ReadValue(&nExportCount, data, &nReadPos);
	unsigned short *nIDs = new unsigned short[nExportCount];
	Utility_ReadData(nIDs, data, nExportCount*2, &nReadPos);

	char *pName = new char[_MAX_PATH];
	m_ExportIDs.resize(nExportCount);
	m_ExportNames.resize(nExportCount);
	m_ExportMovieClip.resize(nExportCount);
	for(unsigned short i = 0; i < nExportCount; i++)
	{
        Utility_ReadNameChar(&pName, data, &nReadPos);
		m_ExportNames[i] = pName;
		m_ExportIDs[i] = nIDs[i];
		m_mapExportIndex[nIDs[i]] = i;
	}
	delete [] pName;
	
	char cTag;
	unsigned int nLength;

	int nID = -1;

	std::map<int, int> mapTag;
	do
	{
		Utility_ReadValue(&cTag, data, &nReadPos);
		Utility_ReadValue(&nLength, data, &nReadPos);
		if(cTag == 0)
			break;
		
		int nDataPos = nReadPos;
		nReadPos+=nLength;

		switch(cTag)
		{
		case CCTT_Texture:
			{
				CoCElementTexture *pElement = new CoCElementTexture();
				pElement->Parse((char*)data+nDataPos, this);
				m_vecTextures.push_back(pElement);
				break;
			}
		case CCTT_Matrix:
			{
				CoCElementMatrix *pElement = new CoCElementMatrix();
				pElement->Parse((char*)data+nDataPos, this);
				m_vecMatrix.push_back(pElement);
				break;
			}
		case CCTT_ColorTransform:
			{
				CoCElementColorTransform *pElement = new CoCElementColorTransform();
				pElement->Parse((char*)data+nDataPos, this);
				m_vecColorTransform.push_back(pElement);
				break;
			}
		case CCTT_Shape:
			{
				CoCElementShape *pElement = new CoCElementShape();
				pElement->Parse((char*)data+nDataPos, this);
				m_vecShap.push_back(pElement);
				m_mapShapIndex[pElement->GetID()] = m_vecShap.size()-1;
				break;
			}
		case CCTT_MovieClip:
			{
				CoCElementMovieClip *pElement = new CoCElementMovieClip();
				pElement->Parse((char*)data+nDataPos, this);
				m_vecMovieClip.push_back(pElement);
				unsigned nIndex = GetMovieClipIndexByID(pElement->GetID());
				if(nIndex != (unsigned short)-1)
				{
					m_ExportMovieClip[nIndex] = pElement;
				}
				break;
			}
		case CCTT_TextField:
		case CCTT_TextField2:
			{
				unsigned short nID;
				Utility_ReadValue(&nID, data, &nDataPos);
				nID = nID;
				break;
			}
		}
		mapTag[cTag]++;

	}while(1);

	return true;
}

void CoCSWF::ExportTexture(const char *pDir)
{
	_mkdir(pDir);
	char szFileName[_MAX_PATH];
	for(size_t i = 0; i < m_vecTextures.size(); i++)
	{
		sprintf(szFileName, "%s/texture_%d.png", pDir, i);
        m_vecTextures[i]->SaveToFile(szFileName);
	}
}

void CoCSWF::ExportDebugData(const char *pDir, int nTag, int nIndex, void *data, int nLength)
{
	//std::string strName = m_pFileName;
	//int nPos = strName.find_last_of("/");
	//strName = strName.substr(nPos+1);
	//
	//char szFileName[_MAX_PATH];
	//
	//_mkdir((std::string(pDir)+"/"+ strName.c_str()+ "/").c_str());

	//sprintf(szFileName, "%s/%s/%d_%d", pDir, strName.c_str(), nTag, nIndex);
	//FILE *fp = fopen(szFileName, "wb");
	//fwrite(data, nLength, 1, fp);
	//fclose(fp);
}

CoCElementTexture *CoCSWF::GetTexture(char cID)
{
	return m_vecTextures[cID];
}

CoCShapeNode *CoCSWF::CreateShapeNodeByID(int nID)
{
	unsigned short nShapeIndex = GetShapIndexByID(nID);
	if(nShapeIndex == (unsigned short)-1)
		return NULL;

	return CreateShapeNodeByIndex(nShapeIndex);
}

CoCShapeNode *CoCSWF::CreateShapeNodeByIndex(int nIndex)
{
	CoCShapeNode *pRoot = CoCShapeNode::create();
	if(nIndex >= m_vecShap.size())
		return NULL;

	CoCElementShape *pShap = m_vecShap[nIndex];
	for(int i = 0; i < pShap->GetShapsCount(); i++)
	{
		ShapInfo &info = *pShap->GetShapInfo(i);		
		CoCImageNode *pNode = CoCImageNode::create(info.count, info.vertex, GetTexture(info.textureID)->getTexture());
		pRoot->addImage(pNode);
	}

	return pRoot;
}

CoCMovieClipNode *CoCSWF::CreateMovieClipByIndex(int nIndex)
{
	CoCElementMovieClip *pMovieClip = m_ExportMovieClip[nIndex];
	if(!pMovieClip)
		return NULL;

	CoCMovieClipNode *pNode = CoCMovieClipNode::create(pMovieClip->GetFrameRate(), pMovieClip->GetFrameCount());
	
	if(pMovieClip == NULL)
		return pNode;
	
	std::vector<FrameInfo> &vecFrameInfo = pMovieClip->GetFrameInfo();
	std::vector<unsigned short> &vecAnimation = pMovieClip->GetAnimationInfo();
	int nFrameIndex = 0;
	for(size_t i = 0; i < vecAnimation.size(); i++)
	{
        for (size_t j = 0; j < vecAnimation[i]; j++)
		{
			pNode->AddFrameInfo(i, &vecFrameInfo[nFrameIndex++]);
		}
	}

	std::vector<unsigned short> &vecShapeID = pMovieClip->GetShapes();
    for (size_t i = 0; i < vecShapeID.size(); i++)
	{
		int nID = vecShapeID[i];
		CoCShapeNode *pShapeNode = (CoCShapeNode *)CreateShapeNodeByID(nID);
		pNode->AddShapdeNode(pShapeNode);
	}

	pNode->updateFrame(0.0f);

	return pNode;
}

CoCMovieClipNode *CoCSWF::CreateMovieClipByName(const char *pName)
{
	unsigned short nIndex = GetMovieClipIndexByName(pName);
	if(nIndex == (unsigned short)-1)
		return NULL;

	return CreateMovieClipByIndex(nIndex);
}

int CoCSWF::GetShapeCount()
{
	return m_vecShap.size();

}

int CoCSWF::GetMovieClipCount()
{
	return m_ExportIDs.size();
}

const char *CoCSWF::GetMovieClipNameByIndex(int nIndex)
{
	return m_ExportNames[nIndex].c_str();
}

unsigned short CoCSWF::GetMovieClipIndexByName(const char *pName)
{	
    for (size_t i = 0; i < m_ExportNames.size(); i++)
	{
		if(stricmp(m_ExportNames[i].c_str(), pName) == 0)
		{
			return i;
		}
	}

	return -1;
}

unsigned short CoCSWF::GetMovieClipIndexByID(unsigned short nID)
{
	std::map<unsigned short, unsigned short>::iterator it = m_mapExportIndex.find(nID);
	
	if(it == m_mapExportIndex.end())
	{
		return -1;
	}
	
	return it->second;
}

unsigned short CoCSWF::GetShapIndexByID(unsigned short nID)
{
	std::map<unsigned short, unsigned short>::iterator it = m_mapShapIndex.find(nID);
	
	if(it == m_mapShapIndex.end())
	{
		return -1;
	}
	
	return it->second;
}
unsigned short CoCSWF::GetShapeIDByIndex(int nIndex)
{
	return m_vecShap[nIndex]->GetID();
}

CoCElementMatrix *CoCSWF::GetTrans(unsigned short nIndex)
{
	if(nIndex == (unsigned short)-1)
		return NULL;
	return m_vecMatrix[nIndex];
}

CoCElementColorTransform *CoCSWF::GetColor(unsigned short nIndex)
{
	if(nIndex == (unsigned short)-1)
		return NULL;
	return m_vecColorTransform[nIndex];
}

CoCElementMovieClip *CoCSWF::GetMovieClipElementByName(const char *pName)
{
	unsigned short nIndex = GetMovieClipIndexByName(pName);
	if(nIndex == (unsigned short)-1)
		return NULL;

	return m_ExportMovieClip[nIndex];
}

CoCElementShape *CoCSWF::GetShapeByID(unsigned short nID)
{
	unsigned short nIndex = GetShapIndexByID(nID);
	if(nIndex == (unsigned short)-1)
		return NULL;

	return m_vecShap[nIndex];
}